#pragma once

#include <iostream>
#include <cmath>
#include <iostream>

/** 
 * Contains all code written for the exercises of 
 * "3D Praktikum: Echtzeitgraphik in C++ und DirectX10"
 */
namespace e3d
{
	template<typename T>
	class Vector3D
	{
	public:
		Vector3D( T x = 0, T y = 0, T z = 0 ): m_x(x), m_y(y), m_z(z){};

		T x() const { return m_x; };
		T y() const { return m_y; };
		T z() const { return m_z; };

		void x( T x ) { m_x = x; };
		void y( T y ) { m_y = y; };
		void z( T z ) { m_z = z; };
		
		/** 
		 * 
		 */
		Vector3D<T> operator-( const Vector3D<T>& v ) const
		{
			return Vector3D( m_x - v.m_x, m_y - v.m_y, m_z - v.m_z );
		};

		void operator-=( const Vector3D<T>& v ) const 
		{
			m_x -= v.m_x;
			m_y -= v.m_y;
			m_z -= v.m_z;
		};

		
		Vector3D<T> operator+( const Vector3D<T>& v ) const 
		{
			return Vector3D( m_x + v.m_x, m_y + v.m_y, m_z + v.m_z );
		};

		void operator+=( const Vector3D<T>& v ) const 
		{
			m_x += v.m_x;
			m_y += v.m_y;
			m_z += v.m_z;
		};

		Vector3D<T> operator*( T scaleFactor ) const
		{
			return Vector3D<T>( m_x * scaleFactor, m_y * scaleFactor, m_z * scaleFactor );
		};

		void operator*=( T scaleFactor ) const
		{
			m_x *= scaleFactor;
			m_y *= scaleFactor; 
			m_z *= scaleFactor;
		};
		
		T length() const
		{
			return sqrt( m_x * m_x + m_y * m_y + m_z * m_z );
		};				

		void normalize()
		{
			const T tmp = length();
			m_x /= tmp;
			m_y /= tmp;
			m_z /= tmp;
		};

		bool isEqual(const Vector3D<T>& v) const
		{
			if( m_x == v.m_x && m_y == v.m_y && m_z == v.m_z )
				return true;
			else
				return false;
		};	
		
		Vector3D<T> crossProd(const Vector3D<T>& v) const
		{
			Vector3D<T> tmp(	m_y * v.m_z - m_z * v.m_y, 
								m_z * v.m_x - m_x * v.m_z, 
								m_x * v.m_y - m_y * v.m_x );
			tmp.normalize();
			return tmp;
		};				

		friend std::ostream& operator<<( std::ostream &os, const Vector3D<T> &obj )
		{
			os << "(" << obj.m_x << "," << obj.m_y << "," << obj.m_z << ")";
			return os;
		};

		// m_blazek stuff
		Vector3D<T> lineMiddle( Vector3D<T> v2 ) const
		{
			Vector3D<T> vMiddle = this* + v2;
			return vMiddle * 0.5;
		}

		Vector3D<T> squareMiddle( const Vector3D<T>& v2, const Vector3D<T>& v3, const Vector3D<T>& v4 ) const
		{
			T x = m_x + v2.x() + v3.x() + v4.x();
			T y = m_y + v2.y() + v3.y() + v4.y();
			T z = m_z + v2.z() + v3.z() + v4.z();
			return Vector3D<T>(x,y,z) * 0.25;
		}
		

	private:
		T m_x,m_y,m_z;
	};

	typedef Vector3D<float> Vector3Df;

}



